<?php
/**
 * Created by PhpStorm.
 * User: Drunk
 * Date: 2017-4-26
 * Time: 1:15
 */

namespace drunk\socket\server;

use drunk\socket\server;

/**
 * Class websocket // websocket 服务器
 * @package drunk\socket\server
 */
class websocket extends server{
    public static $send_as_binary = 0, // 是否以二进制发送 (请注意在实例化之前赋值)
        $byte0 = "\x81";

    function __construct($host) {
        if (self::$send_as_binary) self::$byte0 = "\x82";
        parent::__construct($host);
    }

    public function __destruct() {
        parent::__destruct();
    }

    /**
     * 与websocket客户端握手
     * @param resource $buffer
     * @param string $streams
     * @param array $connection_info
     * @return bool|string
     */
    protected static function handshake($buffer, $streams, &$connection_info){
        $header = self::handshake_header($streams); // 解析请求头为数组并记录之
        if (empty($header['sec-websocket-key'])) return false;
        $key_response = base64_encode(sha1($header['sec-websocket-key'] . '258EAFA5-E914-47DA-95CA-C5AB0DC85B11', true));
        event_buffer_write($buffer, "HTTP/1.1 101 Web Socket Protocol Handshake\r\n" .
            "Upgrade: websocket\r\n" .
            "Connection: Upgrade\r\n" .
            "Sec-WebSocket-Accept: " . $key_response . "\r\n" .
            "\r\n");
        $connection_info['header'] = $header;
        return $key_response;
    }

    /**
     * 储存握手请求头信息
     * @param string $header
     * @param resource $connection
     * @return bool
     */
    private static function handshake_header($header){
        if (!preg_match_all('/^(?:([^:\n]+):)?\s*(.+?)\s*$/ms', $header, $matches, PREG_SET_ORDER)) return false;
        $header = [];
        $method = array_shift($matches);
        if (empty($method[1])) {
            $header['method'] = $method[2];
            $header['path'] = preg_replace('/^\w+\s*|\s*\w+\/[\d\.]+$/', '', $header['method']); // path it
        }
        foreach ($matches as $v) $header[strtolower($v[1])] = $v[2];
        return $header;
    }

    /**
     * websocket帧解码 mark 是否需要处理分片
     * @param string $streams
     * @return null|string
     */
    protected static function decode($streams) {
        static $map_not_string = [
            0x8 => 1,
            0x9 => 1,
            0xa => 1,
        ];
        $opcode = ord($streams[0]) & 0xf;
        $len = ord($streams[1]) & 0x7f;
        if (array_key_exists($opcode, $map_not_string)) return false; // 如果是关闭连接 or ping-pong, 返回false
        if ($len === 126) {
            $masks = substr($streams, 4, 4);
            $data = substr($streams, 8);
        } else if ($len === 127) {
            $masks = substr($streams, 10, 4);
            $data = substr($streams, 14);
        } else {
            $masks = substr($streams, 2, 4);
            $data = substr($streams, 6);
        }
        $decoded = '';
        for ($index = 0; $index < strlen($data); $index++) {
            $decoded .= $data[$index] ^ $masks[$index % 4];
        }
        return $decoded;
    }

    /**
     * 打包为websocket二进制
     * @param string $streams
     * @return string
     */
    protected static function encode($streams) {
        $len = strlen($streams);
        if ($len <= 125) {
            return self::$byte0 . chr($len) . $streams;
        } else if ($len <= 65535){
            return self::$byte0 . chr(126) . pack("n", $len) . $streams;
        } else {
            return self::$byte0 . chr(127) . pack("xxxxN", $len) . $streams;
        }
    }
}